// Learn TypeScript:
//  - https://docs.cocos.com/creator/manual/en/scripting/typescript.html
// Learn Attribute:
//  - https://docs.cocos.com/creator/manual/en/scripting/reference/attributes.html
// Learn life-cycle callbacks:
//  - https://docs.cocos.com/creator/manual/en/scripting/life-cycle-callbacks.html

import { ResManager } from "./custom/ResManager";
import Entity from "./entity";
import { addToGroups, changePos, import_folder, changeToList, getTime, getDictKeyList, rectInflate, getDictValueList } from './support';
import Global from './settings';
import GameEvent from './custom/GameEvent';
import Level from './level';
import SoundManager from './custom/SoundManger';


let HITBOX_OFFSET = Global.HITBOX_OFFSET
let magic_data = Global.magic_data

const {ccclass, property} = cc._decorator;

// @ccclass
export default class Player extends Entity {

    image : cc.Node
    // rect : cc.Rect
    // hitbox : cc.Rect
    status : string

    // movement 
    attacking 
    attack_cooldown 
    attack_time 
    obstacle_sprites 

    // weapon
    create_attack 
    destroy_attack 
    weapon_index 
    weapon 
    can_switch_weapon 
    weapon_switch_time 
    switch_duration_cooldown 
    weapon_data

    // magic 
    create_magic 
    magic_index 
    magic 
    can_switch_magic 
    magic_switch_time 

    // stats
    stats
    max_stats
    upgrade_cost
    health
    energy
    exp
    speed

    // damage timer
    vulnerable
    hurt_time
    invulnerability_duration

    // import a sound
    weapon_attack_sound = null

    animations
    animation
    alpha

    style
    strength
    cost
    level : Level

    constructor(pos,groups,obstacle_sprites,create_attack,destroy_attack,create_magic, level : Level) {

        super(groups)

        pos = changePos(pos)

        this.level = level

        let node = this
        let sp = node.addComponent(cc.Sprite)
        sp.spriteFrame = ResManager.getInstance().getRes()["test"]["player"]
        // @ts-ignore
        node.script = this
        this.image = this
        let width = sp.spriteFrame.getTexture().width
        let height = sp.spriteFrame.getTexture().height
  
		// graphics setup
		this.import_player_assets()

		this.status = 'down'

		// movement 
		this.attacking = false
		this.attack_cooldown = 400
		this.attack_time = null
		this.obstacle_sprites = obstacle_sprites

		// weapon
		this.create_attack = create_attack
		this.destroy_attack = destroy_attack
		this.weapon_index = 0
        this.weapon_data = Global.weapon_data
        let w_data = getDictKeyList(this.weapon_data)
		this.weapon = w_data[this.weapon_index]
		this.can_switch_weapon = false
		this.weapon_switch_time = null
		this.switch_duration_cooldown = 200

		// magic 
		this.create_magic = create_magic
		this.magic_index = 0
		this.magic = getDictKeyList(magic_data)[this.magic_index]
		this.can_switch_magic = false
		this.magic_switch_time = null

		// stats
		this.stats = {'health': 100,'energy':60,'attack': 10,'magic': 4,'speed': 5}
		this.max_stats = {'health': 300, 'energy': 140, 'attack': 20, 'magic' : 10, 'speed': 10}
		this.upgrade_cost = {'health': 100, 'energy': 100, 'attack': 100, 'magic' : 100, 'speed': 100}
		this.health = this.stats['health'] * 0.5
		this.energy = this.stats['energy'] * 0.8
		this.exp = 5000
		this.speed = this.stats['speed']

		// damage timer
		this.vulnerable = false
		this.hurt_time = null
		this.invulnerability_duration = 500

		// import a sound
		this.weapon_attack_sound = "sword"

        // add to group
        addToGroups(this.image, groups)
        
        this.image.setPosition(pos[0]+width/2, pos[1]-height/2)
        this.rect = this.image.getBoundingBox()
        this.hitbox = rectInflate(this.rect, -6, HITBOX_OFFSET['player'])

        cc.systemEvent.on(cc.SystemEvent.EventType.KEY_DOWN, this.input, this)
        cc.systemEvent.on(cc.SystemEvent.EventType.KEY_UP, this.keyUp, this)
        cc.director.on(GameEvent.game_pad_down, (key)=>{
            this.input({keyCode:key})
        }, this)
        cc.director.on(GameEvent.game_pad_up, (key)=>{
            this.keyUp({keyCode:key})
        }, this)
    }

    import_player_assets() {
		this.animations = {'up': [],'down': [],'left': [],'right': [],
			'right_idle':[],'left_idle':[],'up_idle':[],'down_idle':[],
			'right_attack':[],'left_attack':[],'up_attack':[],'down_attack':[]}

        for(let animation in this.animations) {
            let list = import_folder(animation)
            let animList = changeToList(list)
            this.animations[animation] = animList
        }

    }

    input(event) {

        if (!this.attacking && !this.level.game_paused) {

            this.direction.y = 0 
            this.direction.x = 0

            switch(event.keyCode) {

                // movement input
                case cc.macro.KEY.up:
                    this.direction.y = 1
                    this.status = 'up'
                    break
                case cc.macro.KEY.down:
                    this.direction.y = -1
                    this.status = 'down'
                    break

                case cc.macro.KEY.right:
                    this.direction.x = 1
                    this.status = 'right'
                    break
                case cc.macro.KEY.left:
                    this.direction.x = -1
                    this.status = 'left'
                    break

                // attack input 
                case cc.macro.KEY.space:
                    this.attacking = true
                    this.attack_time = getTime()
                    this.create_attack()
                    SoundManager.getInstance().playEffect(this.weapon_attack_sound)
                    break

                // magic input 
                case cc.macro.KEY.ctrl:
                    this.attacking = true
                    this.attack_time = getTime()
                    this.style = getDictKeyList(magic_data)[this.magic_index]
                    this.strength = getDictValueList(magic_data)[this.magic_index]['strength'] + this.stats['magic']
                    this.cost = getDictValueList(magic_data)[this.magic_index]['cost']
                    this.create_magic(this.style,this.strength,this.cost)
                    break

                case cc.macro.KEY.q:
                    if(this.can_switch_weapon) {
                        this.can_switch_weapon = false
                        this.weapon_switch_time = getTime()

                        let keyList = getDictKeyList(this.weapon_data)
                        
                        if (this.weapon_index < keyList.length - 1) {
                            this.weapon_index += 1
                        } else {
                            this.weapon_index = 0
                        }
                        this.weapon = keyList[this.weapon_index]
                    }
                    break

                case cc.macro.KEY.e:
                    if(this.can_switch_magic) {
                        this.can_switch_magic = false
                        this.magic_switch_time = getTime()
                        
                        let keyList = getDictKeyList(magic_data)

                        if (this.magic_index < keyList.length - 1) {
                            this.magic_index += 1
                        } else {
                            this.magic_index = 0
                        }
                        this.magic = keyList[this.magic_index]
                    }
                    break

            }
        }
    }

    keyUp(event) {
        switch(event.keyCode) {
            case cc.macro.KEY.up:
            case cc.macro.KEY.down:
            case cc.macro.KEY.right:
            case cc.macro.KEY.left:
                this.direction.x = 0
                this.direction.y = 0
                break

            case cc.macro.KEY.m:
                if(!this.level.game_paused) {
                    cc.director.emit(GameEvent.key_menu)
                }
                break
        }
    }

    get_status() {
        let isHasIdle = this.status.indexOf("idle") != -1
        let isHasAttack = this.status.indexOf("attack") != -1

        // idle status
        if(this.direction.x == 0 && this.direction.y == 0) {
            if(!isHasIdle && !isHasAttack) {
                this.status = this.status + '_idle'
                isHasIdle = true
            }
        }

        if(this.attacking) {
            this.direction.x = 0
            this.direction.y = 0
            if(!isHasAttack) {
                if(!isHasIdle) {
                    this.status = this.status + '_attack'
                }
                else{
                    this.status = this.status.replace('_idle','_attack')
                }
            }
        } else {
            if(this.status.indexOf('attack') != -1) {
                this.status = this.status.replace('_attack','')
            }
        }
    }

    cooldowns(this) {
        this.current_time = getTime()
        if(this.attacking) {
            let aa = this.weapon_data[this.weapon]['cooldown']
            if(this.current_time - this.attack_time >= this.attack_cooldown + this.weapon_data[this.weapon]['cooldown']){
                this.attacking = false
                this.destroy_attack()
            }
        } 
        if(!this.can_switch_weapon) {
            if(this.current_time - this.weapon_switch_time >= this.switch_duration_cooldown) {
                this.can_switch_weapon = true
            }
        }
        if(!this.can_switch_magic) {
            if(this.current_time - this.magic_switch_time >= this.switch_duration_cooldown) {
                this.can_switch_magic = true
            }
        }
        if(!this.vulnerable) {
            if(this.current_time - this.hurt_time >= this.invulnerability_duration) {
                this.vulnerable = true
            }
        }
    }

    animate() {
        this.animation = this.animations[this.status]

        // loop over the frame index 
        this.frame_index += this.animation_speed
        if(this.frame_index >= this.animation.length) {
            this.frame_index = 0
        }

        // set the image
        let sprite = this.image.getComponent(cc.Sprite)
        
        sprite.spriteFrame = this.animation[Math.floor(this.frame_index)]

        this.rect = this.image.getBoundingBox()
        
        // flicker 
        if(!this.vulnerable) {
            this.alpha = this.wave_value()
            this.image.opacity = this.alpha
        }
        else {
            this.image.opacity = 255
        }
    }

    get_full_weapon_damage(this) {
        this.base_damage = this.stats['attack']
        this.weapon_damage = this.weapon_data[this.weapon]['damage']
        return this.base_damage + this.weapon_damage
    }

    get_full_magic_damage(this) {
        this.base_damage = this.stats['magic']
        this.spell_damage = magic_data[this.magic]['strength']
        return this.base_damage + this.spell_damage
    }

    get_value_by_index(index) {
        // return list(this.stats.values())[index]
        return getDictValueList(this.stats)[index]
    }

    get_cost_by_index(this,index) {
        // return list(this.upgrade_cost.values())[index]

        return getDictValueList(this.upgrade_cost)[index]
    }

    energy_recovery() {
        if(this.energy < this.stats['energy']) {
            this.energy += 0.01 * this.stats['magic']
        } else{
            this.energy = this.stats['energy']
        }
    }

    update(dt) {
        // this.input()
        this.cooldowns()
        this.get_status()
        this.animate()
        this.move(this.stats['speed'])
        this.energy_recovery()
    }

}
